//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include "drvpub.h"
#include "usbapi.h"
#include <string.h>
#include "scsi2.h"
#include "proxy.h"
#include "bulk.h"
#include "usbdbg.h"


// SCSI-2 command data size definitions
#define DATASIZE_INQUIRY       36
#define DATASIZE_MODE_SENSE6   8
#define DATASIZE_MODE_SENSE10  512
#define DATASIZE_REQUEST_SENSE 18

#define VOL_LEN    32
// SCSI-2 direct-access device state
static const UInt8 g_bSenseKey = 0;
static const UInt8 g_bASC = 0;
static const UInt8 g_bASCQ = 0;
static Boolean        g_fInitialized = FALSE;
// Properties of exposed device
static IDeviceDriver    *g_pIStorage;
//static DISK_INFO g_diDiskInfo;

// Whether to report to USB host that exposed device is removable
static UInt32 g_dwRemovable = 1;

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------

#define DEFAULT_SECTOR_SIZE    512

// ----------------------------------------------------------------------------
// Function: BytesPerSector
//     Return number of bytes per sector on exposed device
//
// Parameters:
//     None
// ----------------------------------------------------------------------------

static UInt32 inline BytesPerSector()
{
    DEBUGCHK(g_fInitialized);
    return DEFAULT_SECTOR_SIZE;//g_diDiskInfo.di_bytes_per_sect;
}

// ----------------------------------------------------------------------------
// Function: ByteSwapUlong
//     Same as _byteswap_ulong
//
// Parameters:
//     UInt32 to swap
// ----------------------------------------------------------------------------

static inline UInt32 ByteSwapUlong( UInt32 ul )
{
    PByte pb = (PByte)&ul;
    UInt32 ulRet = pb[3];
    ulRet |= pb[2] << 8;
    ulRet |= pb[1] << 16;
    ulRet |= pb[0] << 24;
    return ulRet;
}

// ----------------------------------------------------------------------------
// Function: ByteSwapUshort
//     Same as _byteswap_ushort
//
// Parameters:
//     UInt16 to swap
// ----------------------------------------------------------------------------

static inline UInt16 ByteSwapUshort( UInt16 us )
{
    PByte pb = (PByte)&us;
    UInt16 usRet = pb[1];
    usRet |= pb[0] << 8;
    return usRet;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiInquiry
//     Process a SCSI-2 INQURY command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiInquiry( PTRANSPORT_COMMAND ptcCommand,
                                 PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiInquiry\n"));

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PSTD_INQUIRYDATA pInquiryData  = (PSTD_INQUIRYDATA) ptdData->DataBlock;
    UInt32 dwResult = EXECUTE_FAIL;
    // don't test if LUN is valid; LUNs are deprecated

    // test if data block is large enough
    if (ptdData->RequestLength < DATASIZE_INQUIRY) {
        DPRINTFN(DBG_CMD, \
        ("Host Requesting less than required! \
         \rptdData->RequestLength < DATASIZE_INQUIRY\n"));
        goto __Exit;
    }
    ptdData->TransferLength = ptdData->RequestLength;

    // peripherial qualifier    = 0x0
    // peripherial device type  = 0x0 (SCSI_DEVICE_DIRECT_ACCESS)
    // RMB                      = 0x0 (non-removable medium)

    pInquiryData->DeviceType = 0x00;
    pInquiryData->RemovableMedia = ((g_dwRemovable ? 1 : 0) << 7);
    pInquiryData->Reserved2 = 0x02;
    pInquiryData->AERC_NACA = 0x02;
    pInquiryData->Reserved4[0] = 0x1F;
    pInquiryData->Reserved4[1] = 0x00;
    pInquiryData->Reserved4[2] = 0x00;
    pInquiryData->Combinated   = 0x00;
    memcpy(pInquiryData->VendorId,  "Elastos ", 8);
    memcpy(pInquiryData->ProductId, "USB Mass Storage", 16);
    memcpy(pInquiryData->ProductRevisionLevel, "v1.01", 4);
    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiModeSense6
//     Process a SCSI-2 MODE SENSE (6) command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiModeSense6( PTRANSPORT_COMMAND ptcCommand,
                                    PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiModeSense6\n"));

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    // test if data block is large enough
    if (ptdData->RequestLength < DATASIZE_MODE_SENSE6) {
        DPRINTFN(DBG_CMD,\
        ("Host Requesting less than required!\
        \rptdData->RequestLength < DATASIZE_MODE_SENSE6\n"));
        goto __Exit;
    }
    Drv_Assert(ptdData->RequestLength <= DEFAULT_DATA_BUFFER_SIZE);
    memset(pbData, 0, ptdData->RequestLength);
    ptdData->TransferLength = ptdData->RequestLength;

    // mode data length
    pbData[0] = 0; // msb
    pbData[1] = 6; // lsb
    // medium type, 9.3.3
    pbData[2] = 0x00;  // default medium type
    // device-specific parameter, 9.3.3
    pbData[3] = 0x00;  // bit 7 = WP (write protected)

    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiModeSense10
//     Process a SCSI-2 MODE SENSE (10) command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiModeSense10( PTRANSPORT_COMMAND ptcCommand,
                                     PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiModeSense10\n"));

    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    // test if data block is large enough
    if (ptdData->RequestLength < DATASIZE_MODE_SENSE10) {
        DPRINTFN(DBG_CMD,\
        ("Host requesting less than required! \
         \rptdData->RequestLength < DATASIZE_MODE_SENSE10\n"));
        goto __Exit;
    }

    // test if DBD (disable block descriptors) bit is high
    if (!(pbCommand[1] & 0x08)) {
        DPRINTFN(DBG_CMD, ("DBD is not enabled\n"));
        goto __Exit;
    }

    // test if PC is 0
    if ((pbCommand[2] & 0xC0) != 0) {
        DPRINTFN(DBG_CMD, ("PC is not 0\n"));
        goto __Exit;
    }

    // test if page code is 0
    if ((pbCommand[2] & 0x3F) != 0) {
        DPRINTFN(DBG_CMD, ("Page code is not 0\n"));
        goto __Exit;
    }
    Drv_Assert(ptdData->RequestLength <= DEFAULT_DATA_BUFFER_SIZE);
    memset(pbData, 0, ptdData->RequestLength);
    ptdData->TransferLength = ptdData->RequestLength;

    // only return the header

    // mode data length
    pbData[0] = 0; // msb
    pbData[1] = 6; // lsb
    // medium type, 9.3.3
    pbData[2] = 0x00; // default medium type
    // device-specific parameter, 9.3.3
    pbData[3] = 0x00; // bit 7 = WP (write protected); read from registry

    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiRequestSense
//     Process a SCSI-2 REQUEST SENSE command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiRequestSense( PTRANSPORT_COMMAND ptcCommand,
                                      PTRANSPORT_DATA ptdData)
{
    DPRINTFN(DBG_CMD, ("ProcessScsiRequestSense\n"));

    const Byte bErrorCode = 0x70;
    const Byte bAdditionalSenseLength = 10;

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    // test if data block is large enough
    if (ptdData->RequestLength < sizeof(SENSE_DATA)) {
        DPRINTFN(DBG_CMD, \
        ("Host Requesting less than required; ptdData->RequestLength < %u\n"\
        ,sizeof(SENSE_DATA)));
        goto __Exit;
    }
    Drv_Assert(ptdData->RequestLength <= DEFAULT_DATA_BUFFER_SIZE);
    memset(pbData, 0, ptdData->RequestLength);
    ptdData->TransferLength = ptdData->RequestLength;

    PSENSE_DATA pSenseData;
    pSenseData = (PSENSE_DATA) pbData;

    pSenseData->ErrorCode = bErrorCode;
    pSenseData->SenseKey = g_bSenseKey;
    pSenseData->AdditionalSenseLength = bAdditionalSenseLength;
    pSenseData->AdditionalSenseCode = g_bASC;
    pSenseData->AdditionalSenseCodeQualifier = g_bASCQ;

    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiSendDiagnostic
//     Process a SCSI-2 SEND DIAGNOSTIC command
//
// Parameters:
//     ptcCommand - command block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiSendDiagnostic( PTRANSPORT_COMMAND ptcCommand )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiSendDiagnostic\n"));

    const Byte bSelfTest = 0x04;

    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    // test if self-test bit is high
    if (!(pbCommand[1] & bSelfTest)) {
        DPRINTFN(DBG_CMD, ("Only self-test is supported\n"));
        goto __Exit;
    }

    // nothing to check
    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiTestUnitReady
//     Process a SCSI-2 TEST UNIT READY command
//
// Parameters:
//     ptcCommand - command block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiTestUnitReady( PTRANSPORT_COMMAND ptcCommand )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiTestUnitReady\n"));

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    // nothing to check
    dwResult = EXECUTE_PASS;
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiRead10
//     Process a SCSI-2 READ (10) command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiRead10( PTRANSPORT_COMMAND ptcCommand,
                                PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiRead10\n"));

    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwLogicalBlockAddress;
    UInt32 dwTransferLength;
    UInt32 dwResult = EXECUTE_FAIL;

//    Boolean fResult = FALSE;
    ECode IoRet;
    PUFI_CB pUfiCb = (PUFI_CB) pbCommand;
    DEBUGCHK(pUfiCb->bReserved1 == 0);

    // don't test if LUN is valid; LUNs are deprecated

    // test if logical block address is valid
    dwLogicalBlockAddress = ByteSwapUlong(pUfiCb->dwLogicalBlockAddress);
    // test if transfer length is valid
    dwTransferLength = ByteSwapUshort(pUfiCb->wTransferLength);

    DPRINTFN(DBG_CMD,\
    ("Starting LBA/sector = %u, Transfer length = %u (sectors)\n",
     dwLogicalBlockAddress, dwTransferLength));

    Drv_Assert((dwTransferLength * BytesPerSector()) <= DEFAULT_DATA_BUFFER_SIZE);
    memset(pbData, 0, (dwTransferLength * BytesPerSector()));
    ptdData->TransferLength = dwTransferLength * BytesPerSector();
    // read from device
    MemoryBuf ebbOutData((Byte*)pbData, ptdData->TransferLength, 0);
    IoRet = g_pIStorage->Read(dwLogicalBlockAddress * BytesPerSector(),\
                                ptdData->TransferLength, &ebbOutData,\
                                NULL);
    if (FAILED(IoRet)) {
        DPRINTFN(DBG_CMD, \
        ("IOCTL_DISK_READ failed; error = %08x\n", IoRet));
        goto __Exit;
    }
    else {
        DPRINTFN(DBG_CMD, \
        ("IOCTL_DISK_READ passed %u bytes read\n",
         pOutData->GetUsed()));
    }

    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiReadCapacity
//     Process a SCSI-2 READ CAPACITY command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------
#define SDMMC_GET_CAPACITY      212 //SD CARD
#define FlashControl_GetInfo    2   //FLASH
static UInt32 ProcessScsiReadCapacity( PTRANSPORT_COMMAND ptcCommand,
                                      PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiReadCapacity\n"));

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwResult = EXECUTE_FAIL;
    Int32  uSize[2] = {0, 0};
    ECode ec = NOERROR;
    MemoryBuf ebbData((Byte *)uSize, sizeof(Int32) * 2);
    // don't test if LUN is valid; LUNs are deprecated
    // test if data block is large enough
    if (ptdData->RequestLength < sizeof(READ_CAPACITY_DATA)) {
        DPRINTFN(DBG_CMD, \
        ("Host Requesting less than required: ptdData->RequestLength < %u\n", \
         sizeof(READ_CAPACITY_DATA)));
        goto __Exit;
    }
    Drv_Assert(ptdData->RequestLength <= DEFAULT_DATA_BUFFER_SIZE);
    memset(pbData, 0, ptdData->RequestLength);
    ptdData->TransferLength = ptdData->RequestLength;
    ec = g_pIStorage->Control(SDMMC_GET_CAPACITY, \
        NULL_MEMORYBUF, &ebbData, NULL);
    if (FAILED(ec)) {
        //TRY DOC GET SIZE
        ec = g_pIStorage->Control(1/*FTLControl_GetSize*/, \
            NULL_MEMORYBUF, &ebbData, NULL);
    }
     // pack logical block address
    PREAD_CAPACITY_DATA pReadCapacityData;
    pReadCapacityData = (PREAD_CAPACITY_DATA) pbData;
    // -1 to return last addressable
    pReadCapacityData->LastLogicalBlockAddress = ByteSwapUlong(\
        (uSize[0] / BytesPerSector() - 1));

    // pack block length in bytes
    pReadCapacityData->BlockLength = ByteSwapUlong(BytesPerSector());
    /*
    DPRINTFN(DBG_CMD, ("u64Size = %d LastLogicalBlockAddress %d BlockLength %d\n", \
                (UInt32)u64Size, pReadCapacityData->LastLogicalBlockAddress, \
                 pReadCapacityData->BlockLength));
    */
    dwResult = EXECUTE_PASS;
__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiReadFormatCapacity
//     Process a SCSI-2 READ FORMAT CAPACITY command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------
static UInt32 ProcessScsiReadFormatCapacity( PTRANSPORT_COMMAND ptcCommand,
                                      PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiReadFormatCapacity\n"));

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwResult = EXECUTE_FAIL;
    UInt32  uSize[2] = {0, 0};
    UInt32 uNumBlocks;
    ECode ec = NOERROR;
    MemoryBuf ebbData((Byte *)uSize, sizeof(Int32) * 2);
    // don't test if LUN is valid; LUNs are deprecated
    // test if data block is large enough
    if (ptdData->RequestLength < sizeof(READ_FORMAT_CAPACITY_DATA)) {
        DPRINTFN(DBG_CMD, \
        ("Host Requesting less than required: ptdData->RequestLength < %u\n", \
         sizeof(READ_CAPACITY_DATA)));
        goto __Exit;
    }
    Drv_Assert(ptdData->RequestLength <= DEFAULT_DATA_BUFFER_SIZE);
    memset(pbData, 0, ptdData->RequestLength);
    ptdData->TransferLength = sizeof(READ_FORMAT_CAPACITY_DATA);//ptdData->RequestLength;
    ec = g_pIStorage->Control(SDMMC_GET_CAPACITY, \
        NULL_MEMORYBUF, &ebbData, NULL);
    if (FAILED(ec)) {
        //TRY DOC GET SIZE
        ec = g_pIStorage->Control(1/*FTLControl_GetSize*/, \
            NULL_MEMORYBUF, &ebbData, NULL);
    }

    PREAD_FORMAT_CAPACITY_DATA pReadFormatCapacityData;
    pReadFormatCapacityData = (PREAD_FORMAT_CAPACITY_DATA) pbData;
    pReadFormatCapacityData->ListLength =
        ByteSwapUlong(sizeof(READ_FORMAT_CAPACITY_DATA) - 4/*exclude Header siz*/);

    uNumBlocks = (uSize[0] / BytesPerSector());
    pReadFormatCapacityData->NumBlocks = ByteSwapUlong(uNumBlocks);
    pReadFormatCapacityData->BlockLength =
        ByteSwapUlong(BytesPerSector()) | (0x02)/*Descripe Code*/;

    pReadFormatCapacityData->FormatNumBlocks = ByteSwapUlong(uNumBlocks);
    pReadFormatCapacityData->FormatBlockLength = ByteSwapUlong(BytesPerSector());

    DPRINTFN(DBG_CMD, ("NumBlocks %08x BlockLength %08x\n",
                pReadFormatCapacityData->NumBlocks,
                 pReadFormatCapacityData->BlockLength));

    dwResult = EXECUTE_PASS;
__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiStartStop
//     Process a SCSI-2 START STOP command
//
// Parameters:
//     ptcCommand - command block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiStartStop( PTRANSPORT_COMMAND ptcCommand )
{
    DPRINTFN(DBG_CMD,("ProcessScsiStartStop\n"));

    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    if (pbCommand[4] & 0x02) {
        // LoEj = 1
        if (pbCommand[4] & 0x01) {
            // Start = 1
            // load medium
            DPRINTFN(DBG_CMD, ("Load medium\n"));
        }
        else {
            // Start = 0
            // unload medium
            DPRINTFN(DBG_CMD, ("Unload Medium\n"));
        }
    }
    else {
        // LoEj = 0
        if (pbCommand[4] & 0x01) {
            // Start = 1
            // start medium
            DPRINTFN(DBG_CMD, ("Start Medium\n"));
        }
        else {
            // Start = 0
            // stop medium
            DPRINTFN(DBG_CMD, ("Stop Medium\n"));
        }
    }

    dwResult = EXECUTE_PASS;
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiWrite10
//     Process a SCSI-2 WRITE (10) command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiWrite10( PTRANSPORT_COMMAND ptcCommand,
                                 PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiWrite10\n"));


    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    PByte pbData = (PByte) ptdData->DataBlock;
    UInt32 dwLogicalBlockAddress;
    UInt32 dwTransferLength;
    UInt32 dwResult = EXECUTE_FAIL;
    ECode IoRet;
    Int32 dwBytesReturned;
//    Boolean fResult = FALSE;
    PUFI_CB pUfiCb = (PUFI_CB) pbCommand;
    DEBUGCHK(pUfiCb->bReserved1 == 0);

    // don't test if LUN is valid; LUNs are deprecated

    // test if logical block address is valid
    dwLogicalBlockAddress = ByteSwapUlong(pUfiCb->dwLogicalBlockAddress);
    // test if transfer length is valid
    dwTransferLength = ByteSwapUshort(pUfiCb->wTransferLength);

    DPRINTFN(DBG_CMD, \
    ("Starting LBA/sector = %u, Transfer length = %u (sectors)\n",\
    dwLogicalBlockAddress, dwTransferLength));

    ptdData->TransferLength = dwTransferLength * BytesPerSector();

    Drv_Assert(ptdData->TransferLength <= DEFAULT_DATA_BUFFER_SIZE);
    MemoryBuf ebbInData((Byte *)pbData, ptdData->TransferLength);


    IoRet = g_pIStorage->Write( dwLogicalBlockAddress *BytesPerSector(), \
                          ebbInData, &dwBytesReturned, NULL );

    if (FAILED(IoRet)) {
        DPRINTFN(DBG_CMD, \
        ("IOCTL_DISK_WRITE failed!\n"));
        goto __Exit;
    }
    else {
        DPRINTFN(DBG_CMD,\
        ("IOCTL_DISK_WRITE passed; %u Write read\n",\
        dwBytesReturned));
    }

    dwResult = EXECUTE_PASS;

__Exit:
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiPreventAllowMediumRemoval
//     Process a SCSI-2 PREVENT ALLOW MEDIUM REMOVAL command
//
// Parameters:
//     ptcCommand - command block wrapper
// ----------------------------------------------------------------------------

static UInt32
ProcessScsiPreventAllowMediumRemoval(
                 PTRANSPORT_COMMAND ptcCommand )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiPreventAllowMediumRemoval!\n"));

    const Byte bPreventBit = 0x01;
    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated

    // test if prevent bit high
    if (pbCommand[4] & bPreventBit) {
        DPRINTFN(DBG_CMD, ("prevent enabled\n"));
    }
    else {
        DPRINTFN(DBG_CMD, ("prevent disabled\n"));
    }

    dwResult = EXECUTE_PASS;
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: ProcessScsiVerify
//     Process a SCSI-2 VERIFY command
//
// Parameters:
//     ptcCommand - command block wrapper
// ----------------------------------------------------------------------------

static UInt32 ProcessScsiVerify( PTRANSPORT_COMMAND ptcCommand )
{
    DPRINTFN(DBG_CMD, ("ProcessScsiVerify!\n"));

//    PByte pbCommand = (PByte) ptcCommand->CommandBlock;
    UInt32 dwResult = EXECUTE_FAIL;

    // don't test if LUN is valid; LUNs are deprecated
/*
    DPRINTFN(DBG_CMD, ("DPO = %u; BytChk = %u; RelAdr = %u\n",
                        pbCommand[1] & (1 << 4),     // DPO bit
                        pbCommand[1] & (1 << 1),     // BytChk bit
                        pbCommand[1] & (1 << 0) ));  // RelAdr
*/
    // fake the verification
    dwResult = EXECUTE_PASS;
    return dwResult;
}

// ----------------------------------------------------------------------------
// Function: STORE_Init
//     Initialize SCSI-2 direct-access device emulator
//
// Parameters:
//     pszContext - driver's configuration key
// ----------------------------------------------------------------------------
ECode STORE_Init(wchar_t *StorageVol)
{
    DPRINTFN(1, ("STORE_Init %S!\n", StorageVol));
    ECode ec = NOERROR;
 //   UInt32 idx;
    if (g_fInitialized == TRUE) {
        DPRINTFN(1, ("A Storage Device Already Opened, please close it!\n"));
        return E_ALREADY_EXIST;
    }
    if (StorageVol == NULL) {
        return E_INVALID_ARGUMENT;
    }
    ec = DzFindService(StorageVol, (IInterface **)&g_pIStorage);

    if (FAILED(ec)) {
        DPRINTFN(DBG_CMD, ("Storage Device [%S] Open Failed!\n", StorageVol));
    }
    else {
        g_fInitialized = TRUE;
    }
//__Exit:
    return ec;
}

// ----------------------------------------------------------------------------
// Function: STORE_Close
//     Close SCSI-2 direct-access device emulator; unitialize, close associated
//     store, and remount exposed partitions
//
// Parameters:
//     None
// ----------------------------------------------------------------------------

ECode STORE_Close()
{
    if (g_fInitialized == TRUE) {
        g_pIStorage->Release();
        g_pIStorage = NULL;
        g_fInitialized = FALSE;
    }

    return NOERROR;
}

// ----------------------------------------------------------------------------
// Function: STORE_IsCommandSupported
//     Determine whether a SCSI-2 command is supported by the SCSI-2
//     direct-access device emulator
//
// Parameters:
//     ptcCommand - command block wrapper
//     pfDataStageRequired - return whether a data stage is required, if
//                           command is supported
//     pdwDirection - return the direction of the command
//     pdwDataSize - return the size of the data stage
// ----------------------------------------------------------------------------

// Determine whether a SCSI-2 command is supported
Boolean STORE_IsCommandSupported( PTRANSPORT_COMMAND ptcCommand,
                               PBoolean pfDataStageRequired,
                               PUInt32 pdwDirection,
                               PUInt32 pdwDataAddr,
                               PUInt32 pdwDataSize )
{
    DPRINTFN(DBG_CMD,("STORE_IsCommandSupported\n"));

    Byte bOpCode = 0;
    Boolean fResult = TRUE;
    PUFI_CB pUfiCb;

    DEBUGCHK(ptcCommand);
    DEBUGCHK(ptcCommand->CommandBlock);
    DEBUGCHK(pfDataStageRequired);
    DEBUGCHK(pdwDirection);
    DEBUGCHK(pdwDataSize);

    if ((g_pIStorage == NULL) || (!g_fInitialized)) {
        DPRINTFN(DBG_CMD, ("Emulator not open! Commands not accepted!\n"));
        fResult = FALSE;
        goto __Exit;
    }

    *pfDataStageRequired = FALSE;
    *pdwDirection = 0;
    *pdwDataSize = 0;
    *pdwDataAddr = 0;

    // command/op code is byte 0
    bOpCode = *((PByte) ptcCommand->CommandBlock);

    pUfiCb = (PUFI_CB) ptcCommand->CommandBlock;

    DPRINTFN(DBG_CMD, ("Check Command OpCode 0x%x\n", bOpCode));

    switch (bOpCode) {
        case SCSI_INQUIRY:
            DPRINTFN(DBG_CMD, ("SCSI_INQUIRY\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataSize = DATASIZE_INQUIRY;
            break;

        case SCSI_MODE_SENSE6:
            DPRINTFN(DBG_CMD, ("SCSI_MODE_SENSE6\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataSize = DATASIZE_MODE_SENSE6;
            break;

        case SCSI_MODE_SENSE10:
            DPRINTFN(DBG_CMD, ("SCSI_MODE_SENSE10\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataSize = DATASIZE_MODE_SENSE10;
            break;

        case SCSI_REQUEST_SENSE:
            DPRINTFN(DBG_CMD, ("SCSI_REQUEST_SENSE\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataSize = DATASIZE_REQUEST_SENSE;
            break;

        case SCSI_SEND_DIAGNOSTIC:
            DPRINTFN(DBG_CMD, ("SCSI_SEND_DIAGNOSTIC\n"));
            break;

        case SCSI_TEST_UNIT_READY:
            DPRINTFN(DBG_CMD, ("SCSI_TEST_UNIT_READY\n"));
            break;

        case SCSI_READ10:
            DPRINTFN(DBG_CMD, ("SCSI_READ10\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataAddr = ByteSwapUlong(pUfiCb->dwLogicalBlockAddress);
            // transfer length is byte 7 + 8
            *pdwDataSize  = ByteSwapUshort(pUfiCb->wTransferLength);
            *pdwDataSize *= BytesPerSector();
            break;

        case SCSI_READ_CAPACITY:
            DPRINTFN(DBG_CMD, ("SCSI_READ_CAPACITY\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataSize = sizeof(READ_CAPACITY_DATA);
            break;

        case SCSI_READ_FORMAT_CAPACITY:
            DPRINTFN(DBG_CMD, ("SCSI_READ_FORMAT_CAPACITY\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_IN;
            *pdwDataSize = sizeof(READ_FORMAT_CAPACITY_DATA);
            break;

        case SCSI_START_STOP:
            DPRINTFN(DBG_CMD, ("SCSI_START_STOP\n"));
            break;

        case SCSI_WRITE10:
            DPRINTFN(DBG_CMD, ("SCSI_WRITE10\n"));
            *pfDataStageRequired = TRUE;
            *pdwDirection = DATA_OUT;
            *pdwDataAddr = ByteSwapUlong(pUfiCb->dwLogicalBlockAddress);
            *pdwDataSize  = ByteSwapUshort(pUfiCb->wTransferLength);
            *pdwDataSize *= BytesPerSector();
            break;

        case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
            DPRINTFN(DBG_CMD, ("SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL\n"));
            break;

        case SCSI_VERIFY:
            DPRINTFN(DBG_CMD, ("SCSI_VERIFY\n"));
            break;

        default:
            DPRINTFN(DBG_ERR, ("Unsupported Command %x\n", bOpCode));
            fResult = FALSE;
            break;
    }

__Exit:

    return fResult;
}

// ----------------------------------------------------------------------------
// Function: STORE_ExecuteCommand
//     Execute and/or emulate the specified command
//
// Parameters:
//     ptcCommand - command block wrapper
//     ptdData - data block wrapper
// ----------------------------------------------------------------------------

UInt32 STORE_ExecuteCommand( PTRANSPORT_COMMAND ptcCommand,
                            PTRANSPORT_DATA ptdData )
{
    DPRINTFN(DBG_CMD, ("STORE_ExecuteCommand!\n"));

    UInt32 dwResult = EXECUTE_FAIL;
    Byte bOpCode = 0;

    if ((g_pIStorage == NULL) || (!g_fInitialized)) {
        DPRINTFN(DBG_CMD, ("Emulator not open! Commands not accepted!\n"))
        dwResult = EXECUTE_FAIL;
        goto __Exit;
    }

    DEBUGCHK(ptcCommand);
    DEBUGCHK(ptcCommand->CommandBlock);


    // command/op code is byte 0
    bOpCode = ((PByte) ptcCommand->CommandBlock)[0];

    DPRINTFN(DBG_CMD, ("Execute Command OpCode %x\n", bOpCode));

    switch (bOpCode) {
        case SCSI_INQUIRY:
            DPRINTFN(DBG_CMD,("Execute SCSI_INQUIRY\n"));
            dwResult = ProcessScsiInquiry(ptcCommand, ptdData);
            break;

        case SCSI_MODE_SENSE6:
            DPRINTFN(DBG_CMD,("Execute SCSI_MODE_SENSE6\n"));
            dwResult = ProcessScsiModeSense6(ptcCommand, ptdData);
            break;

        case SCSI_MODE_SENSE10:
            DPRINTFN(DBG_CMD,("Execute SCSI_MODE_SENSE10\n"));
            dwResult = ProcessScsiModeSense10(ptcCommand, ptdData);
            break;

        case SCSI_REQUEST_SENSE:
            DPRINTFN(DBG_CMD,("Execute SCSI_REQUEST_SENSE\n"));
            dwResult = ProcessScsiRequestSense(ptcCommand, ptdData);
            break;

        case SCSI_SEND_DIAGNOSTIC:
            DPRINTFN(DBG_CMD,("Execute SCSI_SEND_DIAGNOSTIC\n"));
            dwResult = ProcessScsiSendDiagnostic(ptcCommand);
            break;

        case SCSI_TEST_UNIT_READY:
            DPRINTFN(DBG_CMD,("Execute SCSI_TEST_UNIT_READY\n"));
            dwResult = ProcessScsiTestUnitReady(ptcCommand);
            break;

        case SCSI_READ10:
            DPRINTFN(DBG_CMD,("Execute SCSI_READ10\n"));
            dwResult = ProcessScsiRead10(ptcCommand, ptdData);
            break;

        case SCSI_READ_CAPACITY:
            DPRINTFN(DBG_CMD,("Execute SCSI_READ_CAPACITY\n"));
            dwResult = ProcessScsiReadCapacity(ptcCommand, ptdData);
            break;

        case SCSI_READ_FORMAT_CAPACITY:
            DPRINTFN(DBG_CMD,("Execute SCSI_READ_FORMAT_CAPACITY\n"));
            dwResult = ProcessScsiReadFormatCapacity(ptcCommand, ptdData);
            break;

        case SCSI_START_STOP:
            DPRINTFN(DBG_CMD,("Execute SCSI_START_STOP\n"));
            dwResult = ProcessScsiStartStop(ptcCommand);
            break;

        case SCSI_WRITE10:
            DPRINTFN(DBG_CMD,("Execute SCSI_WRITE10\n"));
            dwResult = ProcessScsiWrite10(ptcCommand, ptdData);
            break;

        case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
            DPRINTFN(DBG_CMD,("Execute SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL\n"));
            dwResult = ProcessScsiPreventAllowMediumRemoval(ptcCommand);
            break;

        case SCSI_VERIFY:
            DPRINTFN(DBG_CMD,("Execute SCSI_VERIFY\n"));
            dwResult = ProcessScsiVerify(ptcCommand);
            break;

        default:
            DPRINTFN(DBG_CMD, ("Execute Unsupported Command\n"));
            DEBUGCHK(FALSE);
            break;
    }

__Exit:

    if (dwResult != EXECUTE_PASS) {
        DPRINTFN(DBG_CMD|DBG_ERR, ("Failed to Execute Command 0x%02x\n", bOpCode));
    }

    return dwResult;
}
